/*
*    UNIVERSIDADE DE SÃO PAULO.
*    Author: Marco Aurélio Gerosa (gerosa@ime.usp.br)
*
*    This file is part of Groupware Workbench (http://www.groupwareworkbench.org.br).
*
*    Groupware Workbench is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Lesser General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    Groupware Workbench is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public License
*    along with Swift.  If not, see <http://www.gnu.org/licenses/>.
*/
package br.org.groupwareworkbench.collablet.coop.recommend;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import br.org.groupwareworkbench.collablet.coop.recommend.ClassInfo;
import br.org.groupwareworkbench.collablet.coop.recommend.RecommendDAO;
import br.org.groupwareworkbench.collablet.coop.recommend.RecommendMgrComponent;
import br.org.groupwareworkbench.collablet.coop.recommend.XmlRecommendConfig;
import br.org.groupwareworkbench.core.framework.CollabElementInstance;
import br.org.groupwareworkbench.core.framework.CollabletInstance;
import br.org.groupwareworkbench.core.bd.DAOFactory;
import br.org.groupwareworkbench.core.bd.GenericEntity;

/**
 * Fachada para acesso ao componente
 */
public class RecommendMgrInstance extends CollabElementInstance {

    private RecommendDAO recommendDAO = DAOFactory.get(RecommendDAO.class);
    private int numRelated = 5; // TODO: Não é thread-safe.
    
    public RecommendMgrInstance() {
        super();
    }

    @Override
    public void destroy() {
        this.recommendDAO.deleteByIdInstance(this.getId());
    }

    // --------------------- Buscas ----------------------

    public Collection<Recommend> listAll() {
        return this.recommendDAO.listByIdInstance(this.getId());
    }

    // --------------------- Operações ----------------------

    public void saveRecommend(Recommend recommend) {
        recommend.setIdInstance(this.getId());
        this.recommendDAO.save(recommend, recommend.getId() == 0);
    }

    public void delete(Long idRecommend) {
        this.recommendDAO.deleteById(idRecommend);
    }

    public static Collection<Recommend> listAll(RecommendMgrInstance instance) {
        return instance.listAll();
    }

    public RecommendDAO getRecommendDAO() {
        return recommendDAO;
    }

    public int getNumRelated() {
        return numRelated;
    }

    public void setNumRelated(int numRelated) {
        this.numRelated = numRelated;
    }

    public List<Recommend> getRelated(GenericEntity genericEntity) {
        long idGenericEntity = genericEntity.getId(); 
        List<Recommend> relatedList = recommendDAO.listGenericEntityByNumRelated(this.getId(), genericEntity, getNumRelated());
        List<Recommend> related = new ArrayList<Recommend>();

        for (Recommend recommend : relatedList){
            if(idGenericEntity == recommend.getGenericEntityTo().getId()){
                GenericEntity tempGeneric = recommend.getGenericEntityTo();
                recommend.setGenericEntityTo(recommend.getGenericEntityFrom());
                recommend.setGenericEntityFrom(tempGeneric);
            }
            related.add(recommend);
        }
        return related;
    }
    
    public <E extends GenericEntity> void generateRelated(CollabletInstance collabletMgr, List<E> genericsEntity , E genericEntityFrom){
        XmlRecommendConfig xmlConfig = ((RecommendMgrComponent) this.getComponent()).getXmlConfig();
        List<Recommend> relatedList = new ArrayList<Recommend>();

        for (E genericTo : genericsEntity) {
            if (!genericEntityFrom.equals(genericTo)) {
                Recommend recommend = new Recommend(xmlConfig, genericEntityFrom, genericTo);
                if (recommend.getSimilaritysProduct() == 0) continue;
                recommend.setIdInstance(this.getId());
                if (relatedList.size() < getNumRelated()) {
                    relatedList.add(recommend);
                } else {
                    relatedList = getListMostRelated(relatedList, recommend);
                }
            }
        }
        for (Recommend rec : relatedList){
            recommendDAO.save(rec, true);
        }
    }

    public void deleteAll() {
        this.recommendDAO.deleteByField("idInstance", this.getId());
    }

    // TODO otimizar o algoritmo.
    public <E extends GenericEntity> void calculeAll(List<E> currentList) {
        XmlRecommendConfig xmlConfig = ((RecommendMgrComponent) this.getComponent()).getXmlConfig();
        List<E> toList = currentList;
        Recommend recommend = new Recommend();
        int i = 1;

        for (E from : currentList) {
            List<Recommend> relatedList = new ArrayList<Recommend>();
            for (int j = i ; j < toList.size(); j++) {
                E to = toList.get(j); // TODO: Certificar-se que o acesso é garantidamente não sequencial.
                if (!from.equals(to)) {
                    recommend = new Recommend(xmlConfig, from, to);
                    if (recommend.getSimilaritysProduct() == 0) continue;
                    recommend.setIdInstance(this.getId());
                    if (relatedList.size() < getNumRelated())  {
                        relatedList.add(recommend);
                    } else {
                        relatedList = getListMostRelated(relatedList, recommend);
                    }
                }
            }
            for (Recommend rec : relatedList){
                recommendDAO.save(rec, true);
            }
            i++;
            System.out.println("num de registro : " + i);
        }
    }

    public List<Recommend> getListMostRelated(List<Recommend> relatedList, Recommend recommend){

        double smaller = relatedList.get(0).getSimilaritysProduct();
        int indexSmaller = 0;

        for (int i = 1; i < relatedList.size(); i++) {
            if (smaller > relatedList.get(i).getSimilaritysProduct()) {
                smaller = relatedList.get(i).getSimilaritysProduct();
                indexSmaller = i;
            }
        }
        if (smaller < recommend.getSimilaritysProduct()) {
            relatedList.remove(indexSmaller);
            relatedList.add(indexSmaller, recommend);
        }

        return relatedList;
    }

    public List<ClassInfo> listClassCanBeRecommend(String packageName) {
        
        XmlRecommendConfig xmlConfig = ((RecommendMgrComponent) this.getComponent()).getXmlConfig();
        return xmlConfig.listClassByPackage(packageName);
    }

    public void setEnabledMethodCanRecomend(String packageName, List<String> enabledMethodList) throws SecurityException, NoSuchMethodException {
        XmlRecommendConfig xmlConfig = ((RecommendMgrComponent) this.getComponent()).getXmlConfig();
        xmlConfig.updateStateOfMethod(packageName, enabledMethodList);
    }
}
